#!/usr/bin/env python
#
'''
Created on 2009-3-16

@author: Wang_Yuan
'''

import re
import sys
import copy

import wx
import wx.grid
import wx.lib.mixins.listctrl as listmix

import core

from ManagementPanel_xrc import *
from ExampleEditorDialog import *

class AutoWidthMixListCtrl(wx.ListCtrl, listmix.ListCtrlAutoWidthMixin):
    pass

class ManagementPanel(xrcManagementPanel):
    '''
    Vocabulary management panel. 
    '''
    
    def __init__(self, parent, vocabulary):
        xrcManagementPanel.__init__(self, parent)
        self.vocabulary = vocabulary
        self.__initData()
        self.__initCtrls()
        self.__initEvents()
        if self.wordsList.GetItemCount():
            self.wordsList.SetItemState(0, wx.LIST_STATE_SELECTED, wx.LIST_STATE_SELECTED)
        else:
            self.createNewWordDetail()
        
    def __initData(self):
        self.selectedState = False
        self.selectedListIndex = None
        self.selectedIndex = None
        self.dataListDict = {}
        self.currentWordData = None
        self.exampleIndex = None
        self.wordSet = set(self.vocabulary.words)
        
    def __initCtrls(self):
        self.wordsList.__class__ = AutoWidthMixListCtrl
        listmix.ListCtrlAutoWidthMixin.__init__(self.wordsList)
        
        self.recordList.__class__ = AutoWidthMixListCtrl
        listmix.ListCtrlAutoWidthMixin.__init__(self.recordList)
        self.refreshWholeWordsList()
            
        # Set column titles. 
#        self.meaningList.InsertColumn(0, 'Meanings')
        self.exampleList.InsertColumn(0, 'Original')
        self.exampleList.InsertColumn(1, 'Translation')
        self.recordList.InsertColumn(0, 'Score')
        self.recordList.SetColumnWidth(0, 50)
        self.recordList.InsertColumn(1, 'Date')
        self.recordList.SetColumnWidth(1, 200)
        # Set Nott book titles
        self.merNoteBook.SetPageText(0, 'Meanings')
        self.merNoteBook.SetPageText(1, 'Examples')
        self.merNoteBook.SetPageText(2, 'Exam Records')
        
        self.saveButton.Enable(self.isModifiedDetail())
        
        self.search = wx.SearchCtrl(self.searchPanel, style=wx.TE_PROCESS_ENTER)
        self.search.ShowSearchButton(True)
        self.search.ShowCancelButton(True)
        sizer = self.searchPanel.GetSizer()
        sizer.Add(self.search, proportion=1, flag=wx.EXPAND)
        sizer.Fit(self.searchPanel)
        self.listPanel.Refresh()
        
        
    def __initEvents(self):
        self.meaningFormatErrorBar.Bind(wx.EVT_LEFT_DOWN, self.OnLeft_down_meaningFormatErrorBar)
        self.wordText.Bind(wx.EVT_LEFT_DCLICK, self.OnLeft_left_dclick_wordText)
        self.wordText.Bind(wx.EVT_KILL_FOCUS, self.OnKill_focus_wordText)
        self.pronunciationText.Bind(wx.EVT_LEFT_DCLICK, self.OnLeft_left_dclick_pronunciationText)
        self.pronunciationText.Bind(wx.EVT_KILL_FOCUS, self.OnKill_focus_pronunciationText)
        self.meaningText.Bind(wx.EVT_LEFT_DCLICK, self.OnLeft_left_dclick_meaningText)
        self.meaningText.Bind(wx.EVT_KILL_FOCUS, self.OnKill_focus_meaningText)
        self.wordsList.Bind(wx.EVT_LEFT_DCLICK, self.OnLeft_dclick_wordsList)
        self.exampleList.Bind(wx.EVT_LEFT_DCLICK, self.OnLeft_dclick_exampleList)
        self.exampleList.Bind(wx.EVT_RIGHT_DCLICK, self.OnRight_dclick_exampleList)
        
        self.search.Bind(wx.EVT_TEXT_ENTER, self.OnSearchWord)
        self.search.Bind(wx.EVT_SEARCHCTRL_SEARCH_BTN, self.OnSearchWord)
        self.search.Bind(wx.EVT_SEARCHCTRL_CANCEL_BTN, self.OnCancelSearch)
        pass
    
    def OnClose(self, evt):
        self.askSave()
        evt.Skip()
        
    def OnSearchWord(self, evt):
        print 'search!'
        pattern = self.search.GetValue()
        def displayRange(pattern):
            self.dataListDict.clear()
            listIndex = 0
            for i, word in zip(xrange(len(self.vocabulary.words)), self.vocabulary.words):
                if re.search(pattern, word, re.UNICODE):
                    self.dataListDict[i] = listIndex
                    listIndex += 1
                    yield i
        self.refreshAllWordsInList(displayRange(pattern))
        pass
    
    def OnCancelSearch(self, evt):
        print 'cancel search'
        self.dataListDict.clear()
        self.search.ChangeValue('')
        self.refreshAllWordsInList()
        pass
        
    def OnLeft_dclick_exampleList(self, evt):
        if self.exampleList.GetSelectedItemCount():
            self.editExampleFunc()
        else:
            self.addExampleFunc()
            
    def OnRight_dclick_exampleList(self, evt):
        if self.exampleList.GetSelectedItemCount():
            self.removeExampleFunc()
    
    def OnLeft_down_meaningFormatErrorBar(self, evt):
        message = u'''
About meaning format. 
-----------------------------
The meanings of a word should be input in format below: 
short word class.  meaning1, meaning2, meaning3
For example, 
n. \u56fe\u8868, \u6570\u636e
vt. \u8868\u793a

Meanings can be both your native language or English. 

If your meanings are in wrong format, the input box will be in red, 
and the modification you made after the background turned into red 
will be lost when the word detail is saved. 
'''
        wx.MessageBox(message, 'Help', wx.OK|wx.ICON_INFORMATION)
    
    def OnLeft_dclick_wordsList(self, evt):
        if self.wordsList.GetSelectedItemCount() == 0:
            self.createNewWordDetail()
        else:
            self.removeWord()
    
    def OnList_item_selected_wordsList(self, evt):
        print 'select word list', evt.GetIndex()
        print self.selectedState
        print self.selectedIndex
        if not self.selectedState:
            self.askSave()
        self.selectedListIndex = evt.GetIndex()
        self.selectedIndex = long(self.wordsList.GetItem(self.selectedListIndex, 0).GetText()) - 1
        self.currentWordData = copy.deepcopy(self.vocabulary.data[self.selectedIndex])
        
        self.updateDetail()
        self.removeWordButton.Enable()
        
        wx.CallLater(200, self.setSelectedState)
        
        evt.Skip()
        
    def OnList_item_deselected_wordsList(self, evt):
        print 'deselected word list', evt.m_itemIndex
        print self.selectedState
        print self.selectedIndex
        if self.selectedState:
            self.askSave()
        self.removeWordButton.Enable(self.wordsList.GetSelectedItemCount() > 0)
        wx.CallLater(200, self.setSelectedState)
        
    def OnText_wordText(self, evt):
        if self.currentWordData is not None:
            self.currentWordData.word = self.wordText.GetValue()
            self.saveButton.Enable(self.isModifiedDetail())
    
    def OnLeft_left_dclick_wordText(self, evt):
        if self.selectedIndex is not None:
            self.setEditable(self.wordText, True)
        evt.Skip()
            
    def OnKill_focus_wordText(self, evt):
        print 'kill focus word text'
        if self.wordText.IsEditable() and self.selectedIndex is not None:
            self.finishEdit()
        self.saveButton.Enable(self.isModifiedDetail())
        if evt:
            evt.Skip()
        
    def OnText_pronunciationText(self, evt):
        if self.currentWordData is not None:
            self.currentWordData.pronunciation = self.pronunciationText.GetValue()
            self.saveButton.Enable(self.isModifiedDetail())
            
    def OnLeft_left_dclick_pronunciationText(self, evt):
        if self.selectedIndex is not None:
            self.setEditable(self.pronunciationText, True)
        evt.Skip()
            
    def OnKill_focus_pronunciationText(self, evt):
        if self.pronunciationText.IsEditable() and self.selectedIndex is not None:
            self.finishEdit()
        self.saveButton.Enable(self.isModifiedDetail())
        if evt:
            evt.Skip()
        
    def OnText_meaningText(self, evt):
        if self.currentWordData is not None:
            try:
                self.currentWordData.setMeaningStr(self.meaningText.GetValue())
            except Exception, e:
                self.meaningText.SetBackgroundColour(wx.NamedColor('red'))
                self.meaningFormatErrorBar.Show(True)
            else:
                self.meaningText.SetBackgroundColour(wx.NamedColor('yellow'))
                self.meaningFormatErrorBar.Show(False)
                
            self.Meanings.GetSizer().Layout()
            self.meaningText.Refresh()
            self.saveButton.Enable(self.isModifiedDetail())
            
    def OnLeft_left_dclick_meaningText(self, evt):
        if self.selectedIndex is not None:
            self.setEditable(self.meaningText, True)
        evt.Skip()
            
    def OnKill_focus_meaningText(self, evt):
        print 'kill focus meaning text'
        if self.meaningText.IsEditable() and self.selectedIndex is not None:
            self.finishEdit()
        self.saveButton.Enable(self.isModifiedDetail())
        if evt:
            evt.Skip()
        
    def OnButton_addWordButton(self, evt):
        self.createNewWordDetail()

    def OnButton_removeWordButton(self, evt):
        self.removeWord()
        
    def OnButton_saveButton(self, evt):
        self.finishEdit()
        self.saveVocabulary()
        self.saveButton.Enable(self.isModifiedDetail())

    def OnList_item_selected_exampleList(self, evt):
        self.exampleIndex = evt.GetIndex()
        self.editExample.Enable(True)
        self.removeExample.Enable(True)
    
    def OnList_item_deselected_exampleList(self, evt):
        if not self.exampleList.GetSelectedItemCount():
            self.exampleIndex = None
            self.editExample.Enable(False)
            self.removeExample.Enable(False)
        
    def OnButton_addExample(self, evt):
        self.addExampleFunc()
        
    def OnButton_editExample(self, evt):
        self.editExampleFunc()
        
    def OnButton_removeExample(self, evt):
        self.removeExampleFunc()
        
    def addExampleFunc(self):
        dialog = ExampleEditorDialog(self, wx.ID_ANY, 'Add a new example')
        if dialog.ShowModal() == wx.ID_OK:
            self.currentWordData.examples.add(dialog.getExample())
            self.updateDetail()
        dialog.Destroy()
        
    def editExampleFunc(self):
        if self.exampleIndex is not None:
            dialog = ExampleEditorDialog(self, wx.ID_ANY, 'Add a new example')
            dialog.setExample(self.currentWordData.getExampleList()[self.exampleIndex])
            if dialog.ShowModal() == wx.ID_OK:
                self.currentWordData.examples.add(dialog.getExample())
                self.updateDetail()
            dialog.Destroy()
            
    def removeExampleFunc(self):
        if self.exampleIndex is not None:
            if wx.MessageBox('Are you sure you want to remove this example? ', 
                             'Confirm', 
                             wx.YES_NO|wx.ICON_QUESTION|wx.NO_DEFAULT
                             ) == wx.YES:
                self.currentWordData.examples.remove(self.currentWordData.getExampleList()[self.exampleIndex])
                self.updateDetail()
        
    def askSave(self):
        print 'ask save'
        self.finishEdit()
        if self.isModifiedDetail():
            if wx.MessageBox(message='The word detail is modified. \nDo you want to save the modification into vocabulary?', 
                             caption='Confirm', 
                             style=wx.YES_NO|wx.ICON_QUESTION) == wx.YES:
                self.saveVocabulary()
            else:
                self.restore()
        
    def createNewWordDetail(self):
        if not self.wordsList.GetSelectedItemCount():
            self.askSave()
        if self.selectedIndex is not None:
            self.wordsList.SetItemState(self.selectedIndex, 0, wx.LIST_STATE_SELECTED)
            self.selectedIndex = None
        self.currentWordData = core.WordData()
        self.updateDetail()
        self.setEditable(self.wordText, True)
        self.setEditable(self.pronunciationText, True)
        self.setEditable(self.meaningText, True)
        self.wordText.SetFocus()
        
    def removeWord(self):
        if wx.MessageBox(u'''
Are you sure you want to delete this word?
===============
    %s
===============
        ''' % (unicode(self.currentWordData.word)), 'Warning', wx.YES_NO|wx.ICON_WARNING) == wx.YES:
            self.reallyRemoveWord()
        
    def reallyRemoveWord(self):
        if self.selectedIndex is not None:
            self.wordsList.DeleteItem(self.selectedIndex)
            del self.vocabulary.data[self.selectedIndex]
            self.vocabulary.save()
            self.selectedIndex = None
            self.currentWordData = None
            self.updateDetail()
            self.wordsList.Refresh()
            
        
    def updateDetail(self):
        if self.currentWordData is not None:
            self.wordText.ChangeValue(self.currentWordData.word)
            self.pronunciationText.ChangeValue(self.currentWordData.pronunciation)
            self.meaningText.ChangeValue(self.currentWordData.getMeaningStr())
#            self.meaningList.DeleteAllItems()
#            for meaning in self.currentWordData.getMeaningStr().split('\n'):
#            #for meaning in self.currentWordData.meanings:
#                self.meaningList.InsertStringItem(sys.maxint, meaning)
            self.exampleList.DeleteAllItems()
            for example in self.currentWordData.getExampleList():
                index = self.exampleList.InsertStringItem(sys.maxint, example.originalPart())
                self.exampleList.SetStringItem(index, 1, example.translation if example.translation else '')
            self.exampleList.SetColumnWidth(0, wx.LIST_AUTOSIZE)
            self.exampleList.SetColumnWidth(1, wx.LIST_AUTOSIZE)
            self.exampleList.Refresh()
            
            self.recordList.DeleteAllItems()
            for record in self.currentWordData.records:
                index = self.recordList.InsertStringItem(sys.maxint, unicode(record.score))
                self.recordList.SetStringItem(index, 1, record.getFormatedDate())
        else:
            self.wordText.ChangeValue('')
            self.pronunciationText.ChangeValue('')
            self.meaningText.ChangeValue('')
#            self.meaningList.DeleteAllItems()
            self.exampleList.DeleteAllItems()
            self.recordList.DeleteAllItems()
            
        self.saveButton.Enable(self.isModifiedDetail())
        self.Refresh()
        
    def setEditable(self, ctrl, editable=True):
        ctrl.SetEditable(editable)
        ctrl.SetBackgroundColour(wx.NamedColor('yellow') if editable else self.detailPanel.GetBackgroundColour())
    
    def restore(self):
        if self.selectedIndex is None:
            self.currentWordData = None
        else:
            self.currentWordData = copy.deepcopy(self.vocabulary.data[self.selectedIndex])
            
    def preSave(self):
        pass
    
    def saveVocabulary(self):
        print 'save vocabulary'
        newData = copy.deepcopy(self.currentWordData)
        if newData.word in self.wordSet:
            index = self.vocabulary.words.index(newData.word)
            if index != self.selectedIndex:
                wx.MessageBox(
                              message='The word %s have been existed in the vocabulary. \nYour modification will be merged. ' % (newData.word), 
                              caption='Information', 
                              style=wx.OK|wx.ICON_INFORMATION)
                oldData = self.vocabulary.data[index]
                oldData.absorb(newData)
                if self.selectedIndex is not None:
                    self.wordSet.remove(self.vocabulary.words[self.selectedIndex])
                    del self.vocabulary.data[self.selectedIndex]
                    del self.vocabulary.words[self.selectedIndex]
                    if self.dataListDict:
                        self.selectedListIndex = self.dataListDict.get(self.selectedIndex)
                    else:
                        self.selectedListIndex = self.selectedIndex
                    if self.selectedListIndex is not None:
                        self.wordsList.DeleteItem(self.selectedListIndex)
                self.selectedIndex = index
                if self.dataListDict:
                    self.selectedListIndex = self.dataListDict.get(self.selectedIndex)
                else:
                    self.selectedListIndex = self.selectedIndex
            else:
                self.vocabulary.data[self.selectedIndex] = newData
        else:
            if self.selectedIndex is None:
                # new word
                self.selectedListIndex = self.wordsList.GetItemCount()
                self.selectedIndex = len(self.vocabulary.data)
                self.vocabulary.data.append(newData)
                self.vocabulary.words.append(newData.word)
                self.wordSet.add(newData.word)
            else:
                self.vocabulary.data[self.selectedIndex] = newData
        self.currentWordData = copy.deepcopy(self.vocabulary.data[self.selectedIndex])
        if self.selectedListIndex is not None:
            wx.CallAfter(self.setWordsListItems, self.selectedListIndex, self.selectedIndex, self.vocabulary.data[self.selectedIndex])
        self.vocabulary.save()
        self.updateDetail()
        
    def discardEdit(self):
        self.setEditable(self.wordText, False)
        self.setEditable(self.pronunciationText, False)
        self.setEditable(self.meaningText, False)
        self.updateDetail()
        self.saveButton.Enable(self.isModifiedDetail())
        
    def finishEdit(self):
        if self.currentWordData is None:
            self.currentWordData = core.WordData()
        self.setEditable(self.wordText, False)
        self.setEditable(self.pronunciationText, False)
        self.setEditable(self.meaningText, False)
        self.meaningFormatErrorBar.Show(False)
        self.Meanings.GetSizer().Layout()

        self.currentWordData.word = self.wordText.GetValue()
        self.currentWordData.pronunciation = self.pronunciationText.GetValue()
        try:
            self.currentWordData.setMeaningStr(self.meaningText.GetValue())
        except:
            self.updateDetail()
        self.saveButton.Enable(self.isModifiedDetail())
        
    def refreshWholeWordsList(self):
        self.wordsList.ClearAll()
        # append 3 cols: word, pronunciation, last record
        titles = ['No.', 'Word', 'Pronunciation', 'Last Record']
        
        for i, v in zip(xrange(len(titles)), titles):
            self.wordsList.InsertColumn(i, v)
        
        self.refreshAllWordsInList()
    
    def refreshAllWordsInList(self, showRange=None):
        self.wordsList.DeleteAllItems()
        if showRange is None:
            showRange = xrange(len(self.vocabulary.data))
        for i in showRange:
            index = self.wordsList.InsertStringItem(i, '')
            self.setWordsListItems(index, i, self.vocabulary.data[i])
            if index % 2:
                self.wordsList.SetItemBackgroundColour(index, wx.Color(red=0xee, green=0xee, blue=0xee))
        self.wordsList.SetColumnWidth(0, wx.LIST_AUTOSIZE)
    
    def setWordsListItems(self, index, dataIndex, data):
        if index >= self.wordsList.GetItemCount():
            index = self.wordsList.InsertStringItem(index, '')
        self.wordsList.SetStringItem(index, 0, unicode(dataIndex + 1))
        start = 1
        self.wordsList.SetStringItem(index, start + 0, data.word)
        self.wordsList.SetStringItem(index, start + 1, data.pronunciation)
        if data.records:
            lastRecord = data.records[0]
            self.wordsList.SetStringItem(index, start + 2, data.records[0].getFormatedDate())
#        self.wordsList.SetItemState(index, wx.LIST_STATE_SELECTED, wx.LIST_STATE_SELECTED)
        self.wordsList.Refresh()
    
    def isModifiedDetail(self):
        if self.selectedIndex is None:
            return self.currentWordData is not None and not self.currentWordData.isEmpty()
        else:
            return self.vocabulary.data[self.selectedIndex] != self.currentWordData if self.selectedIndex < len(self.vocabulary.data) else False

    def setSelectedState(self):
        self.selectedState = bool(self.wordsList.GetSelectedItemCount())

class ManagementDialog(wx.Dialog):
    '''
    Vocabulary management dialog
    '''


    def __init__(
            self, parent, ID, title, 
            style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER,
            ):
        '''
        Constructor
        '''
        
        wx.Dialog.__init__(self, parent, ID, title, style=style)
        sizer = wx.BoxSizer(wx.VERTICAL)
        self.panel = ManagementPanel(self, parent.vocabulary)
        sizer.Add(self.panel, proportion=1, flag=wx.ALL|wx.EXPAND, border=5)
        sizer.Fit(self)
        
        self.Bind(wx.EVT_CLOSE, self.panel.OnClose)
        